Примеры.

Редактировал(а) Alexandr Fokin 2025/09/20 11:33

1) Обновление строки по ключу.

Необходимый уровень изоляцииЗапросКомментарий
  Замечание: в приведенных ниже примерах не рассматриваются сценарии с использованием Оптимистичная блокировка.
Механизмы БД и Пессимистичная блокировка.
READ COMMITTEDBEGIN;

-- Проверка

UPDATE accounts
SET balance = balance + @payment
WHERE acctnum = @account1

UPDATE accounts
SET balance = balance - @payment
WHERE acctnum = @account2

COMMIT;
  • Относительное изменения значения (а не константное).
  • Без проверок.

(В данном случае строка заблокируется в момент обновления (update). Чтение (select) с проверкой условия без явного указания updatelock не будет удерживать блокировку, строка может быть изменена после проверки условия).

REPEATABLE READ
MSSQLSET TRANSACTION ISOLATION LEVEL REPEATABLE READ;  
BEGIN TRANSACTION

 if (SELECT balance FROM accounts /*FOR UPDATE*/ where acctnum = @account2 ) < @payment
  -- error
 END IF

UPDATE accounts
SET balance = balance + @payment
WHERE acctnum = @account1

UPDATE accounts
SET balance = balance - @payment
WHERE acctnum = @account2

COMMIT;
 
PostgresBEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;

 if (SELECT balance FROM accounts where acctnum = @account2 ) < @payment
   RAISE EXCEPTION ''
 END IF

UPDATE accounts
SET balance = balance + @payment
WHERE acctnum = @account1

UPDATE accounts
SET balance = balance - @payment
WHERE acctnum = @account2

COMMIT;
 
   
  • Предварительная проверка условия перед изменением строк.
    Гарантия, что значение не будет изменено другими транзакциями после проверки (или их изменение будет обнаружено и приведет к ошибке).
  • В запросе могут использоваться как относительные, так и константные значения, при условии что значение было сформировано после проверки допустимости операции.

(В данном случае чтение (select) удерживает share блокировку, что гарантирует, что строка не будет изменена. Но параллельный переход от sharelock к updatelock приводит к deadlock, поэтому может быть лучше сразу накладывать updatelock).


Данное поведение также может быть реализовано на уровне READ COMMITTED, но для этого нужно явно выполнять чтение с updatelock.
(Причем для данного примера явная блокировка была бы обязательна только для проверяемого аккаунта, с которого выполняется списание).

2) Обновление агрегата.